package org.tulkas.webclient.util;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;

import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Helper class for XML DOM operations. All the methods don't support namespaces.
 * 
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public abstract class DomUtil {
	/**
	 * Convert the XML content of a Document into a string.
	 * 
	 * @param doc
	 * @return
	 * @throws TransformerException
	 * @throws IOException
	 */
	public static String saveToString(Node node) throws TransformerException,
			IOException {
		StringWriter writer = new StringWriter();
		saveToWriter(node, writer);
		String ret = writer.getBuffer().toString();
		writer.close();
		return ret;
	}

	/**
	 * Save the XML content of a Document into a stream
	 * 
	 * @param document
	 * @param os
	 * @throws TransformerException
	 * @throws IOException
	 */
	public static void saveToWriter(Node node, Writer writer)
			throws TransformerException, IOException {
		Source source = new DOMSource(node);
		Result result = new StreamResult(writer);
		Transformer xformer = TransformerFactory.newInstance().newTransformer();

		xformer.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
		xformer.transform(source, result);
	}

	/**
	 * Easy method to get the first sub element from a parent element.
	 * 
	 * @param parent
	 * @return
	 */
	public static Element getFirstSubElement(Node parent) {
		NodeList nodes = parent.getChildNodes();

		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			if (Node.ELEMENT_NODE == node.getNodeType()) {
				return (Element) node;
			}
		}

		return null;
	}

	/**
	 * Easy method to get the first sub element with the specific tag name from a parent element.
	 * 
	 * @param parent
	 * @param tagName
	 * @return
	 */
	public static Element getFirstSubElement(Element parent, String tagName) {
		NodeList nodes = parent.getElementsByTagName(tagName);
		if (nodes.getLength() > 0) {
			return (Element) nodes.item(0);
		}
		return null;
	}

	/**
	 * Easy method to get All the sub elements with the specific tag name from a parent element.
	 * 
	 * @param parent
	 * @param tagName
	 * @return
	 */
	public static Element[] getSubElements(Element parent, String tagName) {
		NodeList nodes = parent.getElementsByTagName(tagName);
		Element[] ret = new Element[nodes.getLength()];
		for (int i = 0; i < nodes.getLength(); i++) {
			ret[i] = (Element) nodes.item(i);
		}

		return ret;
	}

	/**
	 * Easy method to get All the sub elements from a parent element.
	 * 
	 * @param parent
	 * @return
	 */
	public static Element[] getSubElements(Node parent) {
		List<Element> list = new ArrayList<Element>();
		NodeList nodes = parent.getChildNodes();
		for (int i = 0; i < nodes.getLength(); i++) {
			Node node = nodes.item(i);
			if (Node.ELEMENT_NODE == node.getNodeType()) {
				list.add((Element) node);
			}
		}

		return list.toArray(new Element[list.size()]);
	}

	/**
	 * Get the string content from an element.
	 * 
	 * @param element
	 * @return
	 */
	public static String getStringFromElement(Element element) {
		if (element != null) {
			NodeList nodes = element.getChildNodes();
			for (int i = 0; i < nodes.getLength(); i++) {
				Node node = nodes.item(i);
				if (Node.TEXT_NODE == node.getNodeType()) {
					return node.getNodeValue();
				}
			}
		}
		return null;
	}

	/**
	 * Get the string content from the first subelement of a parent element.
	 * 
	 * @param parent
	 * @return
	 */
	public static String getStringFromFirstSubElement(Element parent) {
		Element subEle = getFirstSubElement(parent);
		return getStringFromElement(subEle);
	}

	/**
	 * Get the string content from the first subelement with the speicific tag name of a parent element.
	 * 
	 * @param parent
	 * @param tagName
	 * @return
	 */
	public static String getStringFromFirstSubElement(Element parent,
			String tagName) {
		Element subEle = getFirstSubElement(parent, tagName);
		return getStringFromElement(subEle);
	}

	/**
	 * Get all the string content as an array from the sub elements with the speicific tag name of a parent element.
	 * 
	 * @param parent
	 * @param tagName
	 * @return
	 */
	public static String[] getAllStringsFromSubElements(Element parent,
			String tagName) {
		Element[] eles = getSubElements(parent, tagName);
		String[] ret = new String[eles.length];
		for (int i = 0; i < eles.length; i++) {
			ret[i] = getStringFromElement(eles[i]);
		}
		return ret;
	}
}
